package com.rafael.service.businesslogic.impl;

import java.math.BigDecimal;
import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.rafael.exception.AccesoDatosException;
import com.rafael.exception.CuentaNoExisteException;
import com.rafael.exception.SaldoInsuficienteException;
import com.rafael.persistence.dao.ICuentaDao;
import com.rafael.persistence.dao.IMovimientoDao;
import com.rafael.persistence.dao.hibernate.CuentaDao;
import com.rafael.persistence.entity.Cuenta;
import com.rafael.persistence.entity.Movimiento;
import com.rafael.service.businesslogic.IMovimimentoService;

@Service
public class MovimientoService implements IMovimimentoService {
	
	private IMovimientoDao movimientoDao;
	
	private ICuentaDao cuentaDao;
	
	@Autowired
	public void setMovimientoDao(IMovimientoDao movimientoDao) {
		this.movimientoDao = movimientoDao;
	}
	
	@Required
	public void setCuentaDao(ICuentaDao cuentaDao){
		this.cuentaDao = cuentaDao;
	}

	@Transactional
	public Movimiento persist(Movimiento movimiento) {
		return movimientoDao.makePersistent(movimiento);
	}
	
	@Transactional(readOnly=true)
	public List<Movimiento> getMovimientos() {
		List<Movimiento> list = movimientoDao.findAll();
		return list;
	}

	@Transactional
	public void remove(Movimiento movimiento) {
		movimientoDao.makeTransient(movimiento);
	}

	@Transactional
	public void reload(Movimiento movimiento) {
		movimientoDao.update(movimiento);
	}

	@Transactional
	public void ejecutaTransaccion(Movimiento movimiento) throws CuentaNoExisteException, AccesoDatosException, SaldoInsuficienteException {
		Long cuentaId = movimiento.getCuenta().getCuentaId();
		BigDecimal monto = movimiento.getMonto();
		// tipo = true, deposito
		if(movimiento.getTipo()){
			agregaSaldoCuenta(cuentaId, monto);
		}
		else{
			debitaSaldoCuenta(cuentaId, monto);
		}
		
	}
	
	
	public void agregaSaldoCuenta(Long cuentaId, BigDecimal monto) throws CuentaNoExisteException, AccesoDatosException{
		Cuenta cuenta = cuentaDao.getById(cuentaId);
		if(cuenta==null){
			throw new CuentaNoExisteException("No se encontro una cuenta que corresponda con el Id señalado", new Exception());
		}
		cuenta.setSaldo(cuenta.getSaldo().add(monto.abs()));
		cuentaDao.update(cuenta);
	}
	
	
	public void debitaSaldoCuenta(Long cuentaId, BigDecimal monto) throws SaldoInsuficienteException, CuentaNoExisteException, AccesoDatosException{
		Cuenta cuenta = cuentaDao.getById(cuentaId);
	
		if(cuenta==null){
			throw new CuentaNoExisteException("No se encontro una cuenta que corresponda con el Id señalado", new Exception());
		}
		if(cuenta.getSaldo().compareTo(monto.abs())==-1){
			throw new SaldoInsuficienteException("El saldo en la cuenta a debitar es insuficiente para llevar a cabo la operacion", new Exception());
		}
		cuenta.setSaldo(cuenta.getSaldo().subtract(monto.abs()));
		cuentaDao.update(cuenta);
		
	}
	
	@Transactional(readOnly=true)
	public Movimiento buscaMovimientoPorId(Long movimientoId) throws AccesoDatosException {
		return movimientoDao.getById(movimientoId);
	}
	
	@Transactional
	public Movimiento actualizaMovimiento(Movimiento movAnterior, Movimiento movNuevo) throws SaldoInsuficienteException, CuentaNoExisteException, AccesoDatosException{
		if (movAnterior.getMovimientoId().equals(movNuevo.getMovimientoId())) {
			if (movAnterior.getTipo()) {
				debitaSaldoCuenta(movAnterior.getCuenta().getCuentaId(),
						movAnterior.getMonto());
			} else {
				agregaSaldoCuenta(movNuevo.getCuenta().getCuentaId(),
						movNuevo.getMonto());
			}
			if(movNuevo.getTipo()){
				agregaSaldoCuenta(movNuevo.getCuenta().getCuentaId(), movNuevo.getMonto());
			}
			else{
				debitaSaldoCuenta(movNuevo.getCuenta().getCuentaId(), movNuevo.getMonto());
			}

		}
		movimientoDao.update(movNuevo);
		return movNuevo;
	}

}
